/*
 * @(#)Attribute.java	1.16 07/03/21
 * 
 * Copyright (c) 2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *  
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *  
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *  
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *  
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package com.sun.tools.javac.code;

import java.util.LinkedHashMap;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.DeclaredType;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.util.*;

import static com.sun.tools.javac.code.TypeTags.*;

/** An annotation value.
 *
 *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
 *  you write code that depends on this, you do so at your own risk.
 *  This code and its internal interfaces are subject to change or
 *  deletion without notice.</b>
 */
@Version("@(#)Attribute.java	1.16 07/03/21")
public abstract class Attribute implements AnnotationValue {

    /** The type of the annotation element. */
    public Type type;

    public Attribute(Type type) {
		this.type = type;
    }

    public abstract void accept(Visitor v);//是指Attribute.Visitor,是一个interface, 看后面

    public Object getValue() {
		throw new UnsupportedOperationException();
    }

    public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
		throw new UnsupportedOperationException();
    }


    /** The value for an annotation element of primitive type or String. */
    public static class Constant extends Attribute {
		public final Object value;
		public void accept(Visitor v) { v.visitConstant(this); }
		public Constant(Type type, Object value) {
			super(type);
			this.value = value;
		}
		public String toString() {
			return Constants.format(value, type);
		}
		public Object getValue() {
			return Constants.decode(value, type);
		}
		public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
			if (value instanceof String)
				return v.visitString((String) value, p);
			if (value instanceof Integer) {
				int i = (Integer) value;
				switch (type.tag) {
				case BOOLEAN:	return v.visitBoolean(i != 0, p);
				case CHAR:	return v.visitChar((char) i, p);
				case BYTE:	return v.visitByte((byte) i, p);
				case SHORT:	return v.visitShort((short) i, p);
				case INT:	return v.visitInt(i, p);
				}
			}
			switch (type.tag) {
			case LONG:		return v.visitLong((Long) value, p);
			case FLOAT:		return v.visitFloat((Float) value, p);
			case DOUBLE:	return v.visitDouble((Double) value, p);
			}
			throw new AssertionError("Bad annotation element value: " + value);
		}
    }

    /** The value for an annotation element of type java.lang.Class,
     *  represented as a ClassSymbol.
     */
    public static class Class extends Attribute {
		public final Type type;//让人混淆的type(Attribute中已有一个type)
		public void accept(Visitor v) { v.visitClass(this); }
		public Class(Types types, Type type) {
			super(makeClassType(types, type));
			this.type = type;

			/*例:
			@AnnotationA(f1=10,f2=2,f3=MemberEnterTest.class)
			class AnnotateTest{}

			@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
			@interface AnnotationA{
				int f1();
				int f2();
				//Class f3() default null;
				Class f3() default AnnotationA.class;
			}


			super.type=java.lang.Class<test.memberEnter.MemberEnterTest>
			this.type =test.memberEnter.MemberEnterTest
			super.type=java.lang.Class<test.memberEnter.AnnotationA>
			this.type =test.memberEnter.AnnotationA


			System.err.println("super.type="+super.type);
			System.err.println("this.type ="+this.type);
			*/
		}

        static Type makeClassType(Types types, Type type) {
            Type arg = type.isPrimitive()
                ? types.boxedClass(type).type
                : types.erasure(type);
            return new Type.ClassType(types.syms.classType.getEnclosingType(),
                                      List.of(arg),
                                      types.syms.classType.tsym);
        }

		public String toString() {
			return type + ".class";
		}
		public Type getValue() {
			return type;
		}
		public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
			return v.visitType(type, p);
		}
    }

    /** A compound annotation element value, the type of which is an
     *  attribute interface.
     */
    public static class Compound extends Attribute implements AnnotationMirror {
		/** The attributes values, as pairs.  Each pair contains a
		 *  reference to the accessing method in the attribute interface
		 *  and the value to be returned when that method is called to
		 *  access this attribute.
		 */
		public final List<Pair<MethodSymbol,Attribute>> values;
		public Compound(Type type,
				List<Pair<MethodSymbol,Attribute>> values) {
			super(type);
			this.values = values;
		}
		public void accept(Visitor v) { v.visitCompound(this); }

		/**
		 * Returns a string representation of this annotation.
		 * String is of one of the forms:
		 *     @com.example.foo(name1=val1, name2=val2)
		 *     @com.example.foo(val)
		 *     @com.example.foo
		 * Omit parens for marker annotations, and omit "value=" when allowed.
		 */
		public String toString() {
			StringBuilder buf = new StringBuilder();
			buf.append("@");
			buf.append(type);
			int len = values.length();
			if (len > 0) {
				buf.append('(');
				boolean first = true;
				for (Pair<MethodSymbol, Attribute> value : values) {
					if (!first) buf.append(", ");
					first = false;

					Name name = value.fst.name;
					if (len > 1 || name != name.table.value) {
						buf.append(name);
						buf.append('=');
					}
					buf.append(value.snd);
				}
				buf.append(')');
			}
			return buf.toString();
		}

		public Attribute member(Name member) {
			for (Pair<MethodSymbol,Attribute> pair : values)
				if (pair.fst.name == member) return pair.snd;
			return null;
		}

		public Attribute.Compound getValue() {
			return this;
		}

		public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
			return v.visitAnnotation(this, p);
		}

		public DeclaredType getAnnotationType() {
			return (DeclaredType) type;
		}

		public Map<MethodSymbol, Attribute> getElementValues() {
			Map<MethodSymbol, Attribute> valmap =
			new LinkedHashMap<MethodSymbol, Attribute>();
			for (Pair<MethodSymbol, Attribute> value : values)
				valmap.put(value.fst, value.snd);
			return valmap;
		}
    }

    /** The value for an annotation element of an array type.
     */
    public static class Array extends Attribute {
		public final Attribute[] values;
		public Array(Type type, Attribute[] values) {
			super(type);
			this.values = values;
		}
		public void accept(Visitor v) { v.visitArray(this); }
		public String toString() {
			StringBuilder buf = new StringBuilder();
			buf.append('{');
			boolean first = true;
			for (Attribute value : values) {
				if (!first)
					buf.append(", ");
				first = false;
				buf.append(value);
			}
			buf.append('}');
			return buf.toString();
		}
		public List<Attribute> getValue() {
			return List.from(values);
		}
		public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
			return v.visitArray(getValue(), p);
		}
    }

    /** The value for an annotation element of an enum type.
     */
    public static class Enum extends Attribute {
		/*例:
		@interface AnnotationA{
			EnumTest f6() default EnumTest.A;
		}
		enum EnumTest{
			A,B,C;
		}

		那么type=EnumTest
		value=EnumTest.A
		*/
		public VarSymbol value;
		public Enum(Type type, VarSymbol value) {
			super(type);
			assert value != null;
			this.value = value;
		}
		public void accept(Visitor v) { v.visitEnum(this); }
		public String toString() {
			return value.enclClass() + "." + value;	// qualified name
		}
		public VarSymbol getValue() {
			return value;
		}
		public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
			return v.visitEnumConstant(value, p);
		}
    }

    public static class Error extends Attribute {
		public Error(Type type) {
			super(type);
		}
		public void accept(Visitor v) { v.visitError(this); }
		public String toString() {
			return "<error>";
		}
		public String getValue() {
			return toString();
		}
		public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
			return v.visitString(toString(), p);
		}
    }

    /** A visitor type for dynamic dispatch on the kind of attribute value. */
    public static interface Visitor {
		void visitConstant(Attribute.Constant value);
		void visitClass(Attribute.Class clazz);
		void visitCompound(Attribute.Compound compound);
		void visitArray(Attribute.Array array);
		void visitEnum(Attribute.Enum e);
		void visitError(Attribute.Error e);
    }
}
